
#include <string.h>
#include <stdlib.h>

#include <xdc/std.h>

#include <ti/sdo/ce/Engine.h>

#include <ti/sdo/dmai/Fifo.h>
#include <ti/sdo/dmai/Pause.h>
#include <ti/sdo/dmai/BufTab.h>
#include <ti/sdo/dmai/VideoStd.h>
#include <ti/sdo/dmai/BufferGfx.h>
#include <ti/sdo/dmai/Rendezvous.h>
#include <ti/sdo/dmai/ce/Venc1.h>

#include "dmai_venc.h"

typedef struct {
	Engine_Handle hEngine;
	Venc1_Handle hVe1;
	Buffer_Handle hInBuf;
	Buffer_Handle hOutBuf;
	VIDENC1_DynamicParams dynParams;
	int forceHeader;
} dmai_codec_t;

int
dmai_venc_create (dmai_venc_t *envp)
{
	BufferGfx_Attrs gfxAttrs = BufferGfx_Attrs_DEFAULT;
	Buffer_Attrs bAttrs = Buffer_Attrs_DEFAULT;
	ColorSpace_Type colorSpace = ColorSpace_YUV420PSEMI;
	VIDENC1_Params Params = Venc1_Params_DEFAULT;
	VIDENC1_DynamicParams DynParams = Venc1_DynamicParams_DEFAULT;
	dmai_codec_t *codec;
	Int32 bufSize;
	int err = Dmai_EOK;

	if (envp == NULL)
		return Dmai_EINVAL;

	envp->codec = malloc (sizeof (dmai_codec_t));
	if (envp->codec == NULL)
		return Dmai_ENOMEM;
	codec = (dmai_codec_t *)envp->codec;

	/* Open the codec engine */
	codec->hEngine = Engine_open(envp->engineName, NULL, NULL);
	if (codec->hEngine == NULL) {
		ERR("Failed to open codec engine %s\n", envp->engineName);
		err = Dmai_EINVAL;
		goto err_clean;
	}

	/* Set up codec parameters */
	Params.maxWidth = envp->width;
	Params.maxHeight = envp->height;
	Params.encodingPreset = XDM_HIGH_SPEED;

	if (colorSpace == ColorSpace_YUV420PSEMI) { 
		Params.inputChromaFormat = XDM_YUV_420SP;
	} else {
		Params.inputChromaFormat = XDM_YUV_422ILE;
	}
	Params.reconChromaFormat = XDM_YUV_420SP;
	Params.maxFrameRate = envp->frameRate;

	/* Set up codec parameters depending on bit rate */
	if (envp->bitRate < 0) {
		/* Variable bit rate */
		Params.rateControlPreset = IVIDEO_NONE;

		/*
		* If variable bit rate use a bogus bit rate value (> 0)
		* since it will be ignored.
		*/
		Params.maxBitRate = 0;
	} else {
		/* Constant bit rate */
		Params.rateControlPreset = IVIDEO_STORAGE;
		Params.maxBitRate = envp->bitRate;
	}

	DynParams.targetBitRate = Params.maxBitRate;
	DynParams.inputWidth = Params.maxWidth;
	DynParams.inputHeight = Params.maxHeight;    
	DynParams.refFrameRate = Params.maxFrameRate;
	DynParams.targetFrameRate = Params.maxFrameRate;
	DynParams.interFrameInterval = 0;

	/* Create the video encoder */
	codec->hVe1 = Venc1_create(codec->hEngine, envp->codecName, &Params, &DynParams);

	if (codec->hVe1 == NULL) {
		ERR("Failed to create video encoder: %s\n", envp->codecName);
		err = Dmai_EINVAL;
		goto err_clean;
	}

	codec->dynParams = DynParams;
	codec->forceHeader = 0;

	gfxAttrs.bAttrs.reference = TRUE;
	gfxAttrs.dim.height = envp->height;
	gfxAttrs.dim.width = envp->width;
	gfxAttrs.dim.lineLength = Dmai_roundUp(BufferGfx_calcLineLength(gfxAttrs.dim.width, colorSpace), 32);
	gfxAttrs.dim.x = 0;
	gfxAttrs.dim.y = 0;
	if (colorSpace ==  ColorSpace_YUV420PSEMI) {
		bufSize = gfxAttrs.dim.lineLength * gfxAttrs.dim.height * 3 / 2;
	} else {
		bufSize = gfxAttrs.dim.lineLength * gfxAttrs.dim.height * 2;
	}

	/* Create a table of buffers to use with the device drivers */
	gfxAttrs.colorSpace = colorSpace;

	codec->hInBuf = Buffer_create(bufSize, BufferGfx_getBufferAttrs(&gfxAttrs));
	if (codec->hInBuf == NULL) {
		err = Dmai_ENOMEM;
		goto err_clean;
	}

	bAttrs.reference = TRUE;
	bufSize = Venc1_getOutBufSize(codec->hVe1);
	codec->hOutBuf = Buffer_create (bufSize, &bAttrs);
	if (codec->hOutBuf == NULL) {
		err = Dmai_ENOMEM;
		goto err_clean;
	}
	return Dmai_EOK;

err_clean:
	dmai_venc_delete (envp);
	return err;
}

int
dmai_venc_encode (dmai_venc_t *envp, uint8_t *in, uint8_t *out, uint32_t out_size)
{
	dmai_codec_t *codec;

	if (envp == NULL || envp->codec == NULL)
		return Dmai_EINVAL;
	codec = (dmai_codec_t *)envp->codec;

	Buffer_setUserPtr (codec->hInBuf, (Int8 *)in);
	Buffer_setUserPtr (codec->hOutBuf, (Int8 *)out);
	Buffer_setSize (codec->hOutBuf, out_size);

	if (Venc1_process(codec->hVe1, codec->hInBuf, codec->hOutBuf) < 0) {
		ERR("Failed to encode video buffer\n");
		return Dmai_EFAIL;
	}

	if (codec->forceHeader) {
		VIDENC1_Status encStatus;

		encStatus.size = sizeof(VIDENC1_Status);
		encStatus.data.buf = NULL;

		VIDENC1_control(Venc1_getVisaHandle(codec->hVe1), XDM_SETPARAMS, &codec->dynParams, &encStatus);
		codec->forceHeader = 0;
	}

        return Buffer_getNumBytesUsed(codec->hOutBuf);
}

void
dmai_venc_delete (dmai_venc_t *envp)
{
	dmai_codec_t *codec;

	if (envp == NULL)
		return;
	codec = (dmai_codec_t *)envp->codec;

	if (codec) {
		/* Clean up the thread before exiting */
		if (codec->hInBuf) {
			Buffer_delete(codec->hInBuf);
		}

		if (codec->hOutBuf) {
			Buffer_delete(codec->hOutBuf);
		}

		if (codec->hVe1) {
			Venc1_delete(codec->hVe1);
		}

		if (codec->hEngine) {
			Engine_close(codec->hEngine);
		}
		free (envp->codec);
		envp->codec = NULL;
	}
}

int
dmai_venc_forceHeader (dmai_venc_t *envp)
{
	dmai_codec_t *codec;
	VIDENC1_Status encStatus;
	XDAS_Int32 status;
	VIDENC1_DynamicParams dynParams;

	codec = (dmai_codec_t *)envp->codec;

	encStatus.size = sizeof(VIDENC1_Status);
	encStatus.data.buf = NULL;

	dynParams = codec->dynParams;
	dynParams.generateHeader = XDM_GENERATE_HEADER;

	status = VIDENC1_control(Venc1_getVisaHandle(codec->hVe1), XDM_SETPARAMS, &dynParams, &encStatus);

	if (status != VIDENC1_EOK)
		return -1;
	codec->forceHeader = 1;
	return 0;
}

#include "libavcodec/avcodec.h"

int
avcodec_force_header (AVCodecContext *avctx)
{
	dmai_venc_t *enc = (dmai_venc_t *)avctx->priv_data;
	return dmai_venc_forceHeader (enc);
}
